package cn.hflycolin;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class SyntacticAnalysis {
	private String inputFile;
	private String outputFile;
	private String varfile;
	private String profile;
	private String errfile;
	BufferedReader reader;
	private String[] former;
	private String[] formest;
	private String[] current;
	private String[] next;
	private String[] vars;
	private int lineNum;
	private int vadr;
	boolean flag;
	private String currentPro;
	private int currentlev;
	private String proType;
	private int pvarS;
	private int pvarE;
	private int errtotal;
	private int varslen;

	public String getInputFile() {
		return inputFile;
	}

	public void setInputFile(String inputFile) {
		this.inputFile = inputFile;
	}

	public String getOutputFile() {
		return outputFile;
	}

	public void setOutputFile(String outputFile) {
		this.outputFile = outputFile;
	}

	public SyntacticAnalysis(String inputFile, String outputFile,
			String varfile, String profile, String errfile) throws IOException {
		this.inputFile = inputFile;
		this.outputFile = outputFile;
		this.varfile = varfile;
		this.profile = profile;
		this.errfile = errfile;
		former = null;
		formest=null;
		current = null;
		next = null;
		lineNum = 1;
		vadr = 1;
		pvarS = 0;
		pvarE = 0;
		errtotal = 0;
		vars=new String[100];
		varslen=0;
		FileWriter eraseFile = null;
		eraseFile = new FileWriter(outputFile, false);
		eraseFile.write("");
		eraseFile.close();
		eraseFile = new FileWriter(varfile, false);
		eraseFile.write("");
		eraseFile.close();
		eraseFile = new FileWriter(profile, false);
		eraseFile.write("");
		eraseFile.close();
		eraseFile = new FileWriter(errfile, false);
		eraseFile.write("");
		eraseFile.close();
	}

	private void wf(String context, String outputFile) throws IOException {
		FileWriter writer = new FileWriter(outputFile, true);
		writer.write(context);
		writer.close();
	}

	private void advance(BufferedReader reader) throws IOException {
		String temp = null;
		formest=former;
		former = current;
		current = next;
		while ((temp = reader.readLine()) != null) {
			next = temp.trim().split(" ");
			if (next[0].equals("eoln") == false)
				break;
			else
				lineNum++;
		}
	}

	public void makedys(String inputfile, String outputfile) throws IOException {
		int byteread = 0;
		File oldfile = new File(inputfile);
		InputStream inStream = new FileInputStream(inputfile); 
		FileOutputStream fs = new FileOutputStream(outputfile);
		byte[] buffer = new byte[1444];
		while ((byteread = inStream.read(buffer)) != -1) {
			fs.write(buffer, 0, byteread);
		}
		inStream.close();
	}

	public void analyze() throws IOException, InterruptedException {
		System.out.print("SyntacticAnalysis");
		for (int i = 0; i < 7; i++) {
			System.out.print(".");
			Thread.sleep(70);
		}
		System.out.println("");
		flag = true;
		makedys(inputFile, outputFile);
		reader = new BufferedReader(new InputStreamReader(new FileInputStream(
				this.inputFile)));
		advance(reader);
		advance(reader);
		program();
		if (flag)
			System.out.println("SyntacticAnalysis has been successful!");
		else
			System.out.println("SyntacticAnalysis has been done with error ending!\n"
					+ errtotal + " error(s) has/have been found!");
	}

	private void program() throws IOException {
		block();
	}

	private void block() throws IOException {
		currentPro = "main";
		currentlev = 1;
		proType = "void";
		begin();
		statementList();
		wf("fadr:" + pvarS + "\r\n", profile);
		wf("ladr:" + pvarE + "\r\n", profile);
		pvarS++;
		semicolon();
		implementationList();
		end();
	}

	private void statementList() throws IOException {
		statement();
		statementList2();
	}

	private void statementList2() throws IOException {
		if (current[0].equals(";") && next[0].equals("integer")) {
			semicolon();
			statement();
			statementList2();
		}
	}

	private void statement() throws IOException {
		if (next[0].equals("function") == false) {
			variableStatement();
		} else {
			wf("fadr:" + pvarS + "\r\n", profile);
			wf("ladr:" + pvarE + "\r\n", profile);
			pvarS++;
			functionStatement();
		}
	}

	private void variableStatement() throws IOException {
		integer();
		variable();
	}

	private void variable() throws IOException {
		identifier();
	}

	private void functionStatement() throws IOException {
		integer();
		function();
		currentPro = current[0];
		proType = "function";
		identifier();
		Lbrackets();
		parameter();
		Rbrackets();
		semicolon();
		functionBody();
	}

	private void parameter() throws IOException {
		variable();
	}

	private void functionBody() throws IOException {
		begin();
		statementList();
		semicolon();
		implementationList();
		end();
	}

	private void implementationList() throws IOException {
		implementation();
		implementationList2();
	}

	private void implementationList2() throws IOException {
		if (current[0].equals(";")) {
			advance(reader);
			implementation();
			implementationList2();
		}
	}

	private void implementation() throws IOException {
		if (current[0].equals("read")) {
			read();
		} else if (current[0].equals("write")) {
			write();
		} else if (next[0].equals(":=")) {
			assignment();
		} else if (current[0].equals("if")) {
			condition();
		}
	}

	private void read() throws IOException {
		advance(reader);
		Lbrackets();
		variable();
		Rbrackets();
	}

	private void write() throws IOException {
		advance(reader);
		Lbrackets();
		variable();
		Rbrackets();
	}

	private void assignment() throws IOException {
		variable();
		equal();
		expression();
	}

	private void expression() throws IOException {
		term();
		expression2();
	}

	private void expression2() throws IOException {
		if (current[0].equals("-")) {
			advance(reader);
			term();
			expression2();
		}
	}

	private void term() throws IOException {
		divisor();
		term2();
	}

	private void term2() throws IOException {
		if (current[0].equals("*")) {
			advance(reader);
			divisor();
			term2();
		}
	}

	private void divisor() throws IOException {
		if (current[1].equals("10") && (next[0].equals("(") == false)) {
			variable();
		} else if (current[1].equals("11") && (next[0].equals("(") == false)) {
			constnum();
		} else {
			functionCall();
		}
	}

	private void constnum() throws IOException {
		advance(reader);
	}

	private void functionCall() throws IOException {
		identifier();
		Lbrackets();
		expression();
		Rbrackets();
	}

	private void condition() throws IOException {
		if (current[0].equals("if")) {
			advance(reader);
			cExpression();
			if (current[0].equals("then")) {
				advance(reader);
				implementation();
				if (current[0].equals("else")) {
					advance(reader);
					implementation();
				}
			}
		}
	}

	private void cExpression() throws IOException {
		expression();
		ro();
		expression();
	}

	private void ro() throws IOException {

		if (current[0].equals("=") || current[0].equals("<>")
				|| current[0].equals(">=") || current[0].equals(">")
				|| current[0].equals("<=") || current[0].equals("<")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  Relational operators ERROR!" + "\r\n",
					errfile);
			System.out.println("LINE:" + lineNum + "  Relational operators ERROR!");
			errtotal++;
		}
	}

	private void semicolon() throws IOException {
		if (current[0].equals(";")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  Semicolon ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  Semicolon ERROR!");
			errtotal++;
		}
	}

	private void end() throws IOException {
		if (current[0].equals("end")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  end ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  end ERROR!");
			errtotal++;
		}
	}

	private void begin() throws IOException {
		wf("pname:" + stringFill(currentPro, 16, ' ', true) + "\r\n", profile);
		wf("ptype:" + proType + "\r\n", profile);
		wf("plev:" + currentlev + "\r\n", profile);
		if (current[0].equals("begin")) {
			currentlev++;
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  begin ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  begin ERROR!");
			errtotal++;
		}
	}

	private void integer() throws IOException {

		if (current[0].equals("integer")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  integer ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  integer ERROR!");
			errtotal++;
			advance(reader);
		}
	}

	private void identifier() throws IOException {
		if (current[1].equals("10")) {
			String temp=null;
			if(varslen!=0)
			for (int i = 0; i < varslen; i++) {
				temp=vars[i];
				if(temp.equals(current[0])){advance(reader);return;}
			}
			if (former[0].equals("(") && next[0].equals("")) {
				vars[varslen++]=current[0];
				wf("vname:" + stringFill(current[0], 16, ' ', true) + "\r\n",
						varfile);
				wf("vprov:" + currentPro + "\r\n", varfile);
				wf("vkind:1" + "\r\n", varfile);
				wf("vtype:integer" + "\r\n", varfile);
				wf("vlev:" + currentlev + "\r\n", varfile);
				wf("vadr:" + vadr + "\r\n", varfile);
				vadr++;
				if (pvarS == 0)
					pvarS = 1;
				pvarE++;
			}else if(formest[1].equals("08")||formest[1].equals("09")){
				flag = false;
				wf("LINE:" + lineNum + "  identifier ERROR!" + "\r\n", errfile);
				System.out.println("LINE:" + lineNum + "  identifier ERROR!");
				errtotal++;
				advance(reader);
				return;
			}else if(current[0].equals("0")==false&&current[0].equals("1")==false){
				vars[varslen++]=current[0];
				wf("vname:" + stringFill(current[0], 16, ' ', true) + "\r\n",
						varfile);
				wf("vprov:" + currentPro + "\r\n", varfile);
				wf("vkind:0" + "\r\n", varfile);
				wf("vtype:integer" + "\r\n", varfile);
				wf("vlev:" + currentlev + "\r\n", varfile);
				wf("vadr:" + vadr + "\r\n", varfile);
				vadr++;
				if (pvarS == 0)
					pvarS = 1;
				pvarE++;
			}
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  identifier ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  identifier ERROR!");
			errtotal++;
		}
	}

	private void Rbrackets() throws IOException {
		if (current[0].equals(")")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + (lineNum - 1) + "  Rbrackets ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + (lineNum - 1) + "  Rbrackets ERROR!");
			errtotal++;
			advance(reader);
			//System.exit(1);
		}
	}

	private void Lbrackets() throws IOException {
		if (current[0].equals("(")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + (lineNum - 1) + "  Lbrackets ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + (lineNum - 1) + "  Lbrackets ERROR!");
			errtotal++;
			advance(reader);
			//System.exit(1);
		}
	}

	private void function() throws IOException {

		if (current[0].equals("function")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  function ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  function ERROR!");
			errtotal++;
		}
	}

	private void equal() throws IOException {
		if (current[0].equals(":=")) {
			advance(reader);
		} else {
			flag = false;
			wf("LINE:" + lineNum + "  equal ERROR!" + "\r\n", errfile);
			System.out.println("LINE:" + lineNum + "  equal ERROR!");
			errtotal++;
		}
	}

	public String stringFill(String source, int fillLength, char fillChar,
			boolean isLeftFill) {
		if (source == null || source.length() >= fillLength)
			return source;
		StringBuilder result = new StringBuilder(fillLength);
		int len = fillLength - source.length();
		if (isLeftFill) {
			for (; len > 0; len--) {
				result.append(fillChar);
			}
			result.append(source);
		} else {
			result.append(source);
			for (; len > 0; len--) {
				result.append(fillChar);
			}
		}
		return result.toString();
	}
}
